package com.sailors.common.task.provider.infra.design.common.util.calculate;

import de.congrace.exp4j.Calculable;
import de.congrace.exp4j.ExpressionBuilder;
import de.congrace.exp4j.UnknownFunctionException;
import de.congrace.exp4j.UnparsableExpressionException;
import lombok.extern.slf4j.Slf4j;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Map;
import java.util.Optional;

/**
 * Exp 4 j util
 *
 * @description:
 * @author: cyj
 * @date 2022 -05-16 20:26:04
 *
 *
 */
@Slf4j
public class Exp4jUtil {

    /**
     * Calculate common big decimal
     * Exp4j所支持的运算符包括：
     *
     * 加法：“2 + 2 ”
     * 减法：“2 - 2”
     * 乘法：“2 * 2 ”
     * 除法：“2 / 2 ”
     * 取模：“2 ％ 2”
     * 正负号：“+2 - (-2)”
     * 幂：“2 ^ 2”
     * 内建函数包括：
     * abs: absolute value
     * acos: arc cosine
     * asin: arc sine
     * atan: arc tangent
     * cbrt: cubic root
     * ceil: nearest upper integer
     * cos: cosine
     * cosh: hyperbolic cosine
     * exp: euler's number raised to the power (e^x)
     * floor: nearest lower integer
     * log: logarithmus naturalis (base e)
     * sin: sine
     * sinh: hyperbolic sine
     * sqrt: square root
     * tan: tangent
     * tanh: hyperbolic tangent
     *
     * @param exp    表达式 例如 "x * (y - 1)  - (x + y)"
     * @param params 参数 例如
     * @return the big decimal
     */
    public static BigDecimal calculateCommon(String exp, Map<String, BigDecimal> params) {
        // 获取表达式所有参数
        String[] args = params.keySet().toArray(new String[]{});
        // 构建表达式，并声明变量定义
        ExpressionBuilder builder = new ExpressionBuilder(exp)
                .withVariableNames(args);
        BigDecimal bigDecimal = null;

        try {
            Calculable calc = builder.build();
            params.keySet().stream().forEach(param -> {
                BigDecimal decimal = Optional.ofNullable(params.get(param)).orElse(BigDecimal.ZERO);
                calc.setVariable(param, decimal.doubleValue());
            });
            bigDecimal = new BigDecimal(String.valueOf(calc.calculate()));
        } catch (UnknownFunctionException | UnparsableExpressionException e) {
            e.printStackTrace();
        }
        log.info("计算表达式：{}, 参数：{}, 结果:{}", exp, params, bigDecimal);
        return bigDecimal;
    }

    public static BigDecimal calculateCommon(Exp4jCalculateInfo exp) {

        String expression = exp.getExpression();

        Map<String, BigDecimal> params = exp.params();

        Integer scale = exp.scale();

        return null == scale ? calculateCommon(expression, params) : calculateAndScale(expression, params, scale);
    }

    /**
     * Calculate common and scale big decimal
     *
     * @param exp    exp 表达式
     * @param params params 参数
     * @param scale  scale 保留小数点位数
     * @return the big decimal
     */
    public static BigDecimal calculateAndScale(String exp, Map<String, BigDecimal> params, int scale) {
        BigDecimal calculateCommon = Exp4jUtil.calculateCommon(exp, params);
        if (null == calculateCommon) {
            return calculateCommon;
        }
        return calculateCommon.setScale(scale, RoundingMode.HALF_UP);
    }

}
